package com.commonlib.downloadmgr.base;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;

import android.accounts.NetworkErrorException;
import android.content.Context;


/**
 * File download thread
 * 
 */
public class DownloadThread<T> extends Thread{
	private static final String TAG = DownloadThread.class.getSimpleName();
	private static final long UNIT_RANGE = 1024*1024;
	
	private DownloadBaseJob<T> mJob;
	private Context context;
	
	private RandomAccessFile raf;
	private InputStream is;
	private HttpURLConnection httpURLConnection;
	private File tmpFile;
	private File file;
	
	public DownloadThread(Context context, DownloadBaseJob<T> job){
		this.context = context;
		mJob = job;
	}
	
	@Override
	public void run() {
		Log.v(TAG, "run a thread, thread id:" + getId());
		tmpFile = mJob.getmHelper().getDownloadFileTemp(context, mJob);
		file = mJob.getmHelper().getDownloadFile(context, mJob);
		/**
		 * 初始化下载任务
		 */
		try{
			//网络不可用
			if (!DownloadBaseHelper.isNetworkOk(context)) {
				throw new NetworkErrorException("there is no available network");
			}
			//创建本地下载文件失败
			if (tmpFile == null || file == null) {
				throw new FileNotFoundException("tmpFile or file is not found");
			}
			raf = new RandomAccessFile(tmpFile, "rw");
			long lenRaf = raf.length();
			raf.seek(lenRaf);
			mJob.setmDownloadedSize(lenRaf);
			long contentLength = getContentLength();
			if (contentLength <= 0) {
				throw new NetworkErrorException("illegal ContentLength: " + contentLength);
			}
			mJob.setmTotalSize(contentLength);
			mJob.setState(DownloadBaseJob.STATE_DOWNLOADING, true);
		}catch (FileNotFoundException e) {
			//IO异常，下载失败
			e.printStackTrace();
			mJob.setState(DownloadBaseJob.STATE_FAILED, true);
			mJob.setErrorCode(DownloadBaseJob.ErrorCode.ERROR_IO);
			Log.w(TAG, "Exception: " + e.toString());
		}catch (IOException e) {
			//IO异常，下载失败
			e.printStackTrace();
			mJob.setState(DownloadBaseJob.STATE_FAILED, true);
			mJob.setErrorCode(DownloadBaseJob.ErrorCode.ERROR_IO);
			Log.w(TAG, "Exception: " + e.toString());
		}catch (NetworkErrorException e) {
			//网络不可用
			e.printStackTrace();
			mJob.setState(DownloadBaseJob.STATE_FAILED, true);
			mJob.setErrorCode(DownloadBaseJob.ErrorCode.ERROR_NETWORK);
			Log.w(TAG, "Exception: " + e.toString());
		}catch (Exception e){
			//未知异常
			e.printStackTrace();
			mJob.setState(DownloadBaseJob.STATE_FAILED, true);
			mJob.setErrorCode(DownloadBaseJob.ErrorCode.ERROR_OTHER);
			Log.w(TAG, "Exception: " + e.toString());
		}
		finally{
			if (DownloadBaseJob.STATE_SUCCESS == mJob.getState()) {
				//TODO
			}
		}
		
		/**
		 * 开始下载
		 */
		try{
			downloading();
		}catch (IOException e) {
			//IO异常，下载失败
			e.printStackTrace();
			mJob.setState(DownloadBaseJob.STATE_FAILED, true);
			mJob.setErrorCode(DownloadBaseJob.ErrorCode.ERROR_IO);
			Log.w(TAG, "Exception: " + e.toString());
			//重置本地下载文件至上一个分段状态
			if (raf != null) {
				long newLength = mJob.getmDownloadedSize() - mJob.getmDownloadedSize()%(UNIT_RANGE);
				try {
					raf.setLength(newLength);
				} catch (IOException e1) {
					e1.printStackTrace();
					Log.e(TAG, "failed to cut local download file, IOException:" + e1.toString());
				}
				Log.v(TAG, "cut local download file as length " + newLength + "bytes");
			}else{
				Log.v(TAG, "failed to cut local download file, outputStream is null!");
			}
		} catch (Exception e){
			//下载过程异常
			e.printStackTrace();
			mJob.setState(DownloadBaseJob.STATE_FAILED, true);
			mJob.setErrorCode(DownloadBaseJob.ErrorCode.ERROR_OTHER_DOWNLOADING);
			Log.w(TAG, "Exception: " + e.toString());
		} finally{
			if (raf != null) {
				try {
					raf.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (httpURLConnection != null) {
				httpURLConnection.disconnect();
			}
			mJob.onFinish();
		}
		
	}

	/**
	 * 递归调用下载
	 * @throws Exception
	 */
	private void downloading() throws Exception{
		Log.v(TAG, "do downloading");
		boolean isLastSegment = false;
		//可操作的状态，下载中和暂停, 并且当前分段不是最后一个分段
		while(!isLastSegment && (DownloadBaseJob.STATE_PAUSED == mJob.getState()
				|| DownloadBaseJob.STATE_DOWNLOADING == mJob.getState())){
			//初始化当前下载分段，建立连接
			String rangeEnd;
			boolean isWifiState = DownloadBaseHelper.isWiFiConnected(context);
			if (isWifiState) {
				isLastSegment = true;
			}else{
				isLastSegment = (mJob.getmTotalSize() - mJob.getmDownloadedSize()) <= UNIT_RANGE;
			}
			rangeEnd = isLastSegment?"":String.valueOf((mJob.getmDownloadedSize()+UNIT_RANGE-1));
			Log.v(TAG, "downloading, range:" + mJob.getmDownloadedSize() + "-" + rangeEnd);
			Map<String, String> headerParameter = mJob.getmHelper().getHeadParameter();
			URL url = new URL(mJob.getUrl());
			httpURLConnection = (HttpURLConnection)url.openConnection();
			httpURLConnection.setAllowUserInteraction(true);
			httpURLConnection.setRequestMethod("GET");
			httpURLConnection.setConnectTimeout(15 * 1000);
			httpURLConnection.setReadTimeout(15 * 1000);
			//Set header parameters
			httpURLConnection.setRequestProperty("Range", "bytes=" + mJob.getmDownloadedSize() + "-" + rangeEnd);
			if (headerParameter != null) {
				for (String key : headerParameter.keySet()) {
					httpURLConnection.setRequestProperty(key, headerParameter.get(key));
				}
			}
			
			Log.v(TAG, "Url:" + mJob.getUrl());
			Log.v(TAG, "RequestProperties: " + httpURLConnection.getRequestProperties().toString());
			Log.v(TAG, "ResponseHeaders:" + httpURLConnection.getHeaderFields().toString());
			
			if (mJob.getmTotalSize() <= 0) {
				long lenContent = -1;
				Map<String, List<String>> headers = httpURLConnection.getHeaderFields();
				List<String> listCR = headers.get("Content-Range");
				if (listCR != null) {
					String strCR = listCR.get(0);
					int iStart = strCR.indexOf("/")+1;
					if (iStart > 0 && iStart < strCR.length()) {
						String strValue = strCR.substring(iStart);
						try{
							lenContent = Long.valueOf(strValue);
							Log.v(TAG, "Capture lenContent from field: Content-Range");
						}catch (NumberFormatException e) {
							e.printStackTrace();
							Log.w(TAG, "Exception: " + e.toString());
							lenContent = -1;
						}
					}
				}
				if (lenContent <= 0) {
					lenContent = httpURLConnection.getContentLength();
					Log.v(TAG, "Capture lenContent from field: Content-Length");
				}
				Log.v(TAG, "init contentLength with" + lenContent);
				mJob.setmTotalSize(lenContent);
			}
			//服务器返回的contentlength异常
			if (mJob.getmTotalSize() <= 0) {
				throw new NetworkErrorException("contentLength is illegal:" + mJob.getmTotalSize());
			}
			//本地已存在同样大小对应的下载文件
			if (mJob.getmDownloadedSize() == mJob.getmTotalSize()) {
				Log.v(TAG, "cached temp file length is equal with contentLength");
				if(tmpFile.renameTo(file)){
					Log.v(TAG, "Sucessfully rename tmpFile as file, set state as STATE_SUCCESS");
					mJob.setState(DownloadBaseJob.STATE_SUCCESS, true);
				}else{
					tmpFile.delete();
					raf.seek(0);
					mJob.setmDownloadedSize(0);
					Log.v(TAG, "falied to rename tmpFile as file, delete cached tmpfile and reDownloadload");
					//初始化成功，状态设置为下载中
					mJob.setState(DownloadBaseJob.STATE_FAILED, true);
				}
				return;
			}
			//获取输入流，下载数据
			is = httpURLConnection.getInputStream();
			
			byte[] buf = new byte[1024*8];
			int read = 0;
			//可操作的状态，下载中和暂停
			while(DownloadBaseJob.STATE_PAUSED == mJob.getState()
					|| DownloadBaseJob.STATE_DOWNLOADING == mJob.getState()){
				read = is.read(buf);
				if(read==-1) {
					if (isLastSegment) {
						//inputStream 读取完毕，判断下载内容的合法性
						if(mJob.getmDownloadedSize() == mJob.getmTotalSize()) {
							if(tmpFile.renameTo(file)){
								Log.d(TAG, " download success! -->  ");
								mJob.setState(DownloadBaseJob.STATE_SUCCESS, true);
							}else{
								Log.d(TAG, " download failed! --> failed to rename tempFile as file");
								tmpFile.delete();
								throw new Exception("failed to rename tempFile as file");
							}
						}else{
							Log.d(TAG, " download failed! -->" + "curSize is " + mJob.getmDownloadedSize() + ", length is " + mJob.getmTotalSize());
							tmpFile.delete();
							throw new Exception("current size is not equal with content length");
						}
					}else{
						//递归调用继续下载下一个分段
						downloading();
					}
				}else{
					//写数据到临时文件
					raf.write(buf,0,read);
					mJob.setmDownloadedSize(mJob.getmDownloadedSize() + read);
				}
				//暂停状态，线程休眠
				if (DownloadBaseJob.STATE_PAUSED == mJob.getState()) {
					synchronized (mJob) {
						try{
							Log.v(TAG, "current thread is waiting, thread id:" + getId());
							mJob.wait();
						}catch (InterruptedException e) {
							Log.v(TAG, "current thread is Interrupted, thread id:" + getId());
						}
					}
				}
			}
		}
	}
	
	private long getContentLength(){
		long retval = -1;
		URL url;
		try {
			url = new URL(mJob.getUrl());
			HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection();
			httpURLConnection.setRequestMethod("HEAD");
			retval = httpURLConnection.getContentLength();
			Log.v(TAG, "getContentLength, Url : " + mJob.getUrl());
			Log.v(TAG, "RequestProperties : " + httpURLConnection.getRequestProperties().toString());
			Log.v(TAG, "ResponseHeaders : " + httpURLConnection.getHeaderFields().toString());
		} catch (Exception e) {
			e.printStackTrace();
			Log.w(TAG, "Exception: " + e.toString());
		}
		return retval;
	}
	
}
